#include "U2DataStreamMemory.h"



U2EG_NAMESPACE_USING


U2DataStreamMemory::U2DataStreamMemory(void* pMem, size_t inSize, bool freeOnClose, bool readOnly, Endian endian)
: U2DataStream(static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)), endian)
{
    mData = mPos = static_cast<u2uchar*>(pMem);
    mSize = inSize;
    mEnd = mData + mSize;
    mFreeOnClose = freeOnClose;
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(const U2String& name, void* pMem, size_t inSize, 
                                   bool freeOnClose, bool readOnly, Endian endian)
                                   : U2DataStream(name, static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)), endian)
{
    mData = mPos = static_cast<u2uchar*>(pMem);
    mSize = inSize;
    mEnd = mData + mSize;
    mFreeOnClose = freeOnClose;
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(U2DataStream& sourceStream, 
                                   bool freeOnClose, bool readOnly)
                                   : U2DataStream(static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)))
{
    // Copy data from incoming stream
    mSize = sourceStream.size();
    if (mSize == 0 && !sourceStream.eof())
    {
        // size of source is unknown, read all of it into memory
        U2String contents = sourceStream.getAsString();
        mSize = contents.size();
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        memcpy(mData, contents.data(), mSize);
        mEnd = mData + mSize;
    }
    else
    {
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        mEnd = mData + sourceStream.read(mData, mSize);
        mFreeOnClose = freeOnClose;
    }
    mFlipEndian = sourceStream.isFlipEndian();
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(U2DataStreamPtr& sourceStream, 
                                   bool freeOnClose, bool readOnly)
                                   : U2DataStream(static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)))
{
    // Copy data from incoming stream
    mSize = sourceStream->size();
    if (mSize == 0 && !sourceStream->eof())
    {
        // size of source is unknown, read all of it into memory
        U2String contents = sourceStream->getAsString();
        mSize = contents.size();
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        memcpy(mData, contents.data(), mSize);
        mEnd = mData + mSize;
    }
    else
    {
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        mEnd = mData + sourceStream->read(mData, mSize);
        mFreeOnClose = freeOnClose;
    }
    mFlipEndian = sourceStream->isFlipEndian();
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(const U2String& name, U2DataStream& sourceStream, 
                                   bool freeOnClose, bool readOnly)
                                   : U2DataStream(name, static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)))
{
    // Copy data from incoming stream
    mSize = sourceStream.size();
    if (mSize == 0 && !sourceStream.eof())
    {
        // size of source is unknown, read all of it into memory
        U2String contents = sourceStream.getAsString();
        mSize = contents.size();
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        memcpy(mData, contents.data(), mSize);
        mEnd = mData + mSize;
    }
    else
    {
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        mEnd = mData + sourceStream.read(mData, mSize);
        mFreeOnClose = freeOnClose;
    }
    mFlipEndian = sourceStream.isFlipEndian();
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(const U2String& name, const U2DataStreamPtr& sourceStream, 
                                   bool freeOnClose, bool readOnly)
                                   : U2DataStream(name, static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)))
{
    // Copy data from incoming stream
    mSize = sourceStream->size();
    if (mSize == 0 && !sourceStream->eof())
    {
        // size of source is unknown, read all of it into memory
        U2String contents = sourceStream->getAsString();
        mSize = contents.size();
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        memcpy(mData, contents.data(), mSize);
        mEnd = mData + mSize;
    }
    else
    {
        mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
        mPos = mData;
        mEnd = mData + sourceStream->read(mData, mSize);
        mFreeOnClose = freeOnClose;
    }
    mFlipEndian = sourceStream->isFlipEndian();
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(size_t inSize, bool freeOnClose, bool readOnly, Endian endian)
: U2DataStream(static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)), endian)
{
    mSize = inSize;
    mFreeOnClose = freeOnClose;
    mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
    mPos = mData;
    mEnd = mData + mSize;
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::U2DataStreamMemory(const U2String& name, size_t inSize, 
                                   bool freeOnClose, bool readOnly, Endian endian)
                                   : U2DataStream(name, static_cast<u2uint16>(readOnly ? READ : (READ | WRITE)), endian)
{
    mSize = inSize;
    mFreeOnClose = freeOnClose;
    mData = U2_ALLOC_T(u2uchar, mSize, MEMCATEGORY_GENERAL);
    mPos = mData;
    mEnd = mData + mSize;
    assert(mEnd >= mPos);
}
//-----------------------------------------------------------------------
U2DataStreamMemory::~U2DataStreamMemory()
{
    close();
}
//-----------------------------------------------------------------------
size_t U2DataStreamMemory::read(void* buf, size_t count)
{
    size_t cnt = count;
    // Read over end of memory?
    if (mPos + cnt > mEnd)
        cnt = mEnd - mPos;
    if (cnt == 0)
        return 0;

    assert (cnt<=count);

    memcpy(buf, mPos, cnt);
    mPos += cnt;
    return cnt;
}
//---------------------------------------------------------------------
size_t U2DataStreamMemory::write(const void* buf, size_t count)
{
    size_t written = 0;
    if (isWriteable())
    {
        written = count;
        // we only allow writing within the extents of allocated memory
        // check for buffer overrun & disallow
        if (mPos + written > mEnd)
            written = mEnd - mPos;
        if (written == 0)
            return 0;

        memcpy(mPos, buf, written);
        mPos += written;
    }
    return written;
}
//-----------------------------------------------------------------------
size_t U2DataStreamMemory::readLine(char* buf, size_t maxCount, 
                                  const U2String& delim)
{
    // Deal with both Unix & Windows LFs
    bool trimCR = false;
    if (delim.find_first_of('\n') != U2String::npos)
    {
        trimCR = true;
    }

    size_t pos = 0;

    // Make sure pos can never go past the end of the data 
    while (pos < maxCount && mPos < mEnd)
    {
        if (delim.find(*mPos) != U2String::npos)
        {
            // Trim off trailing CR if this was a CR/LF entry
            if (trimCR && pos && buf[pos-1] == '\r')
            {
                // terminate 1 character early
                --pos;
            }

            // Found terminator, skip and break out
            ++mPos;
            break;
        }

        buf[pos++] = *mPos++;
    }

    // terminate
    buf[pos] = '\0';

    return pos;
}
//-----------------------------------------------------------------------
size_t U2DataStreamMemory::skipLine(const U2String& delim)
{
    size_t pos = 0;

    // Make sure pos can never go past the end of the data 
    while (mPos < mEnd)
    {
        ++pos;
        if (delim.find(*mPos++) != U2String::npos)
        {
            // Found terminator, break out
            break;
        }
    }

    return pos;

}
//-----------------------------------------------------------------------
void U2DataStreamMemory::skip(long count)
{
    size_t newpos = (size_t)( ( mPos - mData ) + count );
    assert( mData + newpos <= mEnd );        

    mPos = mData + newpos;
}
//-----------------------------------------------------------------------
void U2DataStreamMemory::seek( size_t pos )
{
    assert( mData + pos <= mEnd );
    mPos = mData + pos;
}
//-----------------------------------------------------------------------
size_t U2DataStreamMemory::tell(void) const
{
    //mData is start, mPos is current location
    return mPos - mData;
}
//-----------------------------------------------------------------------
bool U2DataStreamMemory::eof(void) const
{
    return mPos >= mEnd;
}
//-----------------------------------------------------------------------
void U2DataStreamMemory::close(void)    
{
    if (mFreeOnClose && mData)
    {
        U2_FREE(mData, MEMCATEGORY_GENERAL);
        mData = 0;
    }
}